import re
import wx
import threading
import subprocess
import custom_events
import gvf_snakes

class SnakeInterfaceWindow(wx.Window):
    def __init__ (self, parent, doodle):
        super(SnakeInterfaceWindow, self).__init__(parent)
        self.parent = doodle
        
        #val = wx.FloatingPointValidator()
        
        # Initialize the Controls
        GVFIterTxt = wx.StaticText(self, label='GVF Iterations')
        self.GVFIterTxt = wx.TextCtrl(self, value='800')
        MuTxt = wx.StaticText(self, label='Mu')
        self.MuTxt = wx.TextCtrl(self, value='0.2')
        GSecTxt = wx.StaticText(self, label='Timeout (s)')
        self.GSecTxt = wx.TextCtrl(self, value='15')
        GradTxt = wx.StaticText(self, label='Gradient Type')
        methods = ['Scharr', 'Sobel']
        self.GradCBox = wx.ComboBox(self, choices=methods, style=wx.CB_READONLY)
        self.GradCBox.SetStringSelection(methods[1])
        #TODO add listbox with grad methods
        #also possibly del_t factor
        
        SnakeIterTxt = wx.StaticText(self, label='Snake Iterations')
        self.SnakeIterTxt = wx.TextCtrl(self, value='81')
        ATxt = wx.StaticText(self, label='Alpha')
        self.ATxt = wx.TextCtrl(self, value='0.05')
        BTxt = wx.StaticText(self, label='Beta')
        self.BTxt = wx.TextCtrl(self, value='0.0')
        GTxt = wx.StaticText(self, label='Gamma')
        self.GTxt = wx.TextCtrl(self, value='1.0')
        KTxt = wx.StaticText(self, label='Kappa')
        self.KTxt = wx.TextCtrl(self, value='0.6')
        RTxt = wx.StaticText(self, label='Radius')
        self.RTxt = wx.TextCtrl(self, value='15')
        SSecTxt = wx.StaticText(self, label='Timeout (s)')
        self.SSecTxt = wx.TextCtrl(self, value='15')
        MinTxt = wx.StaticText(self, label='Min Dist')
        self.MinTxt = wx.TextCtrl(self, value='0.5')
        MaxTxt = wx.StaticText(self, label='Max Dist')
        self.MaxTxt = wx.TextCtrl(self, value='2')
        
        GVFButton = wx.Button(self, label="Compute GVF")
        GVFButton.Bind(wx.EVT_BUTTON, self.OnGVFPress)
        SnakeButton = wx.Button(self, label="Deform Snake")
        SnakeButton.Bind(wx.EVT_BUTTON, self.OnSnakePress)
        
        self.Bind(custom_events.EVT_POSTBACK, self.handlePostBack)

        #Create Sizer and add controls to it
        sizer = wx.FlexGridSizer(15,2,0,0)
        
        sizer.Add(GradTxt,0,0)
        sizer.Add(self.GradCBox,0,0)
        sizer.Add(GVFIterTxt,0,0)
        sizer.Add(self.GVFIterTxt,0,0)
        sizer.Add(MuTxt,0,0)
        sizer.Add(self.MuTxt,0,0)
        sizer.Add(GSecTxt,0,0)
        sizer.Add(self.GSecTxt,0,0)
        
        sizer.Add(GVFButton,0,0)
        sizer.Add(wx.StaticText(self, label=''),0,0)
        
        sizer.Add(SnakeIterTxt,0,0)
        sizer.Add(self.SnakeIterTxt,0,0)
        sizer.Add(ATxt,0,0)
        sizer.Add(self.ATxt,0,0)
        sizer.Add(BTxt,0,0)
        sizer.Add(self.BTxt,0,0)
        sizer.Add(GTxt,0,0)
        sizer.Add(self.GTxt,0,0)
        sizer.Add(KTxt,0,0)
        sizer.Add(self.KTxt,0,0)
        sizer.Add(RTxt,0,0)
        sizer.Add(self.RTxt,0,0)
        sizer.Add(MinTxt,0,0)
        sizer.Add(self.MinTxt,0,0)
        sizer.Add(MaxTxt,0,0)
        sizer.Add(self.MaxTxt,0,0)
        sizer.Add(SSecTxt,0,0)
        sizer.Add(self.SSecTxt,0,0)
        
        sizer.Add(SnakeButton,0,0)
        sizer.Add(wx.StaticText(self, label=''),0,0)
        
        self.SetSizer(sizer)

    def OnGVFPress (self, evt):
        imgloc = '"' + self.parent.imgLoc + '"'
        iter = self.GVFIterTxt.GetValue()
        mu = self.MuTxt.GetValue()
        method = str(self.GradCBox.GetSelection())
        timeout = float(self.GSecTxt.GetValue())
        
        cmdstr = r'"..\Snake_Stepper\Debug\Snake_Stepper.exe"' + ' ' + imgloc + ' ' + method + ' ' + iter + ' ' + mu
        print cmdstr
        gvfPID = subprocess.Popen(cmdstr)
        gvfTimer = threading.Timer(timeout, self.killProcess, args=[gvfPID])
        gvfTimer.start()
        gvfThread = threading.Thread(target=self.manageProcess, args=[gvfPID, 0])
        gvfThread.start();
        
    def OnSnakePress (self, evt):
        iter = self.SnakeIterTxt.GetValue()
        r = self.RTxt.GetValue()
        a = self.ATxt.GetValue()
        b = self.BTxt.GetValue()
        g = self.GTxt.GetValue()
        k = self.KTxt.GetValue()
        mind = self.MinTxt.GetValue()
        maxd = self.MaxTxt.GetValue()
        timeout = float(self.SSecTxt.GetValue())
        #add mindist/maxdist
        cmdstr = r'"..\Snake_Stepper\Debug\Snake_Stepper.exe"' + ' ' + iter + ' ' + r + ' ' + a + ' ' + b + ' ' + k + ' ' + g + ' ' + mind + ' ' + maxd
        print cmdstr
        SnakePID = subprocess.Popen(cmdstr)
        SnakeTimer = threading.Timer(timeout, self.killProcess,  args=[SnakePID])
        SnakeTimer.start()
        SnakeThread = threading.Thread(target=self.manageProcess, args=[SnakePID, 1])
        SnakeThread.start()
    
    '''Block (should be on a separate thread) until the C++ code finishes and then call the GUI postback update events.
    Take care and check if the process exited cleanly'''
    def manageProcess (self, pid, calltype):
        pid.wait()
        print pid.returncode
        evt = custom_events.ProcessPostBackEvent(calltype=calltype,success=pid.returncode)
        wx.PostEvent(self, evt)
        
    '''Handle the callback from the C++ process and post the results to the main window'''    
    def handlePostBack (self, evt):
        #check if process exited correctly, might raise a dialog box...
        if evt.success != 0:
            return
        #GVF case
        if evt.calltype == 0:
            self.updateGVF()
        #Snake case
        elif evt.calltype == 1:
            self.updateSnake()
            
        
    '''Kill the process if the timeout limit is reached and the process has not exited cleanly yet 
    (a threading.Timer class calls this) 
    -- might cause an issue if the person presses the button more than once... (shouldnt, the pid would be different)'''
    def killProcess (self, pid):
        if (pid.poll() is None):
            print "terminating process"
            pid.terminate()
            
    def updateGVF (self):
        print "GVF"
        gvfu = list()
        gvfv = list()
        gooddata = 0
        rows = -1
        cols = -1
        channels = -1
        lines = open("gvf.dat", "r").readlines()
        match = re.match(r'U: (\d+) (\d+) (\d)', lines[0])
        if match:
            gooddata = 1
            rows = int(match.group(1))
            cols = int(match.group(2))
            channels = int(match.group(3))
            lines.pop(0)
            gvfu = lines[0:rows]
            #gvfu[:] = [e.split() for e in gvfu]
            tempu = list()
            for uline in gvfu:
                templist = uline.split()
                templist = [float(e) for e in templist]
                tempu.append(templist)
            gvfu = tempu
            del lines[0:rows+1]
            gvfv = lines
            tempv = list()
            for vline in gvfv:
                templist = vline.split()
                templist = [float(e) for e in templist]
                tempv.append(templist)
            gvfv = tempv
            self.parent.dispGVF(gvf_snakes.GVField(rows, cols, channels, gvfu, gvfv))
        else:
            print "bad gvf data"
    
    def updateSnake (self):
        print "SNAKE"
        gooddata = 0
        snakes = list()
        currentsnake = ""
        for line in open("snake.dat", "r").readlines():
            match = re.match(r'Step (\d+)', line)
            if match:
                gooddata = 1
                currentsnake = gvf_snakes.Snake(match.group(1))
                snakes.append(currentsnake)
                #print match.group(1)
            elif gooddata == 1:
                match = re.match(r'(\d+\.?\d+?) (\d+\.?\d+?)', line)
                if match:
                    currentsnake.addpt(match.group(1), match.group(2))
                    #print currentsnake.pts[len(currentsnake.pts)-1][0], currentsnake.pts[len(currentsnake.pts)-1][1]
                else:
                    gooddata = 0
                    print "whoops: " + line #shouldnt hit this....
            else:
                print "uhoh: " + line #edge case? faulty data? shouldnt call this...
        self.parent.dispSnake(snakes)
        #for snake in snakes:
            #print "SNAKE!!!", snake.step
            #for pt in snake.pts:
                #print pt[0], pt[1]
